package org.altervista.cp.thinbcgen;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;

@SuppressWarnings("unchecked")
public abstract class AbstractGenerator {

	protected Class getGeneratedClass(String generatedClassName, Object[] args) {
		Class generatedClass = null;
		synchronized (_CLASS_CACHE) {
			generatedClass = (Class) _CLASS_CACHE.get(generatedClassName);
			if (generatedClass == null) {
				generatedClass = defineClass(generatedClassName, generateClass(
						generatedClassName, args));
				_CLASS_CACHE.put(generatedClassName, generatedClass);
			}
		}
		return generatedClass;
	}

	protected byte[] generateClass(String generatedClassName, Object[] args) {
		return _realGen.generateClass(generatedClassName, args);
	}

	protected abstract Constructor getGeneratorConstructor();

	protected AbstractGenerator(Class targetClass, Object[] args) {
		_targetClass = targetClass;

		if (_TL.get() == null) {
			_TL.set(this);
			_realGen = (AbstractGenerator) newInstance(
					getGeneratorConstructor(), new Object[] { _targetClass,
							args });
		} else {
			_TL.set(null);
		}
	}

	protected Class defineClass(String className, byte[] bytes) {
		Method m;
		try {
			m = ClassLoader.class.getDeclaredMethod("defineClass",
					new Class<?>[] { String.class, byte[].class, int.class,
							int.class });
			m.setAccessible(true);
			return (Class) m.invoke(_targetClass.getClassLoader(),
					new Object[] { className, bytes, 0, bytes.length });
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	protected byte[] generateClass() {
		return _realGen.generateClass();
	}

	protected static final Object newInstance(Constructor constructor,
			Object[] paramValues) {
		try {
			if (paramValues == null) {
				paramValues = NO_PARAM_VALUES;
			}
			return constructor.newInstance(paramValues);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	protected Class _targetClass;
	protected AbstractGenerator _realGen;
	protected Constructor _realGenConstructor;

	protected static final ThreadLocal<AbstractGenerator> _TL = new ThreadLocal<AbstractGenerator>();
	protected static final HashMap _CLASS_CACHE = new HashMap();

	protected static final Object[] NO_PARAM_VALUES = new Object[0];
	protected static final Class[] NO_PARAM_TYPES = new Class[0];

}
